package com.opencvjava.support.util;

import com.opencvjava.support.other.ImgDialog;
import org.opencv.core.Mat;

import java.util.Map;

import static com.opencvjava.support.util.CommonUtils.classPath;
import static org.opencv.imgcodecs.Imgcodecs.IMREAD_COLOR;
import static org.opencv.imgcodecs.Imgcodecs.imread;

public class CvUtils {

    public static Mat filter2D(Mat src, int[][] kernal) {
        int kernalSize = kernal.length;
        if (kernalSize % 2 == 0) {
            throw new RuntimeException("kernal大小必须是单数");
        }
        int kernalRadius = kernalSize / 2;
        Mat dst = new Mat(src.size(), src.type());
        int rows = src.rows();
        int cols = src.cols();
        int channels = src.channels();
        byte[] data = new byte[rows * cols * channels];
        byte[] dstData = new byte[data.length];
        src.get(0, 0, data);
        int index, curIndex;
        int color, k;
        int[] sum = new int[channels];
        int opRows = rows - kernalRadius;
        int opCols = cols - kernalRadius;
        for (int row = kernalRadius; row < opRows; row++) {
            for (int col = kernalRadius; col < opCols; col++) {
                index = row * cols * channels + col * channels;
                for (int i = -kernalRadius; i <= kernalRadius; i++) {
                    for (int j = -kernalRadius; j <= kernalRadius; j++) {
                        curIndex = (row + i) * cols * channels + (col + j) * channels;
                        k = kernal[kernalRadius + i][kernalRadius + j];
                        for (int channel = 0; channel < channels; ++channel) {
                            color = data[curIndex + channel] & 0xff;
                            sum[channel] += color * k;
                        }
                    }
                }
                for (int channel = 0; channel < channels; ++channel) {
                    dstData[index + channel] = (byte) (clamp(sum[channel]) & 0xff);
                    sum[channel] = 0;
                }
            }
        }
        dst.put(0, 0, dstData);
        return dst;
    }

    public static Mat lutMIM(Mat src, Mat dst, Map<Integer, Integer> lookUpTable) {
        byte[] data = new byte[src.rows() * src.cols() * src.channels()];
        src.get(0, 0, data);
        byte[] dstData = lutBIB(data, lookUpTable);
        dst.put(0, 0, dstData);
        return dst;
    }

    public static Mat lutMIM(Mat src, Map<Integer, Integer> lookUpTable) {
        Mat dst = new Mat(src.size(), src.type());
        byte[] data = new byte[src.rows() * src.cols() * src.channels()];
        src.get(0, 0, data);
        byte[] dstData = lutBIB(data, lookUpTable);
        dst.put(0, 0, dstData);
        return dst;
    }

    public static byte[] lutBIB(byte[] src, Map<Integer, Integer> lookUpTable) {
        byte[] dst;
        if (src == null || src.length == 0) {
            return null;
        } else {
            dst = new byte[src.length];
            for (int i = 0; i < src.length; i++) {
                dst[i] = (byte) (lookUpTable.get(src[i] & 0xff) & 0xff);
            }
        }
        return dst;
    }

    public static byte[] lutB2B(byte[] src, Map<Byte, Byte> lookUpTable) {
        byte[] dst;
        if (src == null || src.length == 0) {
            return null;
        } else {
            dst = new byte[src.length];
            for (int i = 0; i < src.length; i++) {
                dst[i] = lookUpTable.get(src[i]);
            }
        }
        return dst;
    }

    public static int[] lutI2I(int[] src, Map<Integer, Integer> lookUpTable) {
        int[] dst;
        if (src == null || src.length == 0) {
            return null;
        } else {
            dst = new int[src.length];
            for (int i = 0; i < src.length; i++) {
                dst[i] = lookUpTable.get(src[i]);
            }
        }
        return dst;
    }

    public static String matDataStr(Mat mat) {
        StringBuilder sb = new StringBuilder();
        sb.append("[\n");
        if (mat != null && !mat.empty()) {
            int rows = mat.rows();
            int cols = mat.cols();
            int channels = mat.channels();
            int aRowLength = cols * channels;
            byte[] data = new byte[rows * cols * channels];
            mat.get(0, 0, data);
            if (channels == 1) {
                for (int i = 0; i < data.length; i++) {
                    sb.append(data[i] & 0xFF);
                    if ((i + 1) % aRowLength == 0) {
                        sb.append(";\n");
                    } else {
                        sb.append(", ");
                    }
                }
            } else if (channels > 1) {
                for (int i = 0; i < data.length; i++) {
                    if (i % channels == 0) {
                        sb.append('(');
                    }
                    sb.append(data[i] & 0xFF);
                    if (i % channels == (channels - 1)) {
                        sb.append(')');
                    }
                    if ((i + 1) % aRowLength == 0) {
                        sb.append(";\n");
                    } else {
                        sb.append(", ");
                    }
                }
            }
        }
        sb.append("]\n");
        return sb.toString();
    }

    public static Mat mat(String fold, String filename, int flag) {
        return imread(classPath(new StringBuilder().append(fold).append('/')
                .append(filename).toString()), flag);
    }

    public static Mat mat(String fold, String filename) {
        return mat(fold, filename, IMREAD_COLOR);
    }

    public static Mat mat(String filename) {
        return mat(filename, IMREAD_COLOR);
    }

    public static Mat mat(String filename, int flag) {
        return imread(classPath(filename), flag);
    }

    public static void imshow(String title, Mat mat) {
        new ImgDialog(title, mat).imshow();
    }

    public static int clamp(double rgb) {
        int intRgb = (int) rgb;
        if (intRgb < 0)
            return 0;
        if (intRgb > 255)
            return 255;
        return intRgb;
    }
}
